using System.Collections;
using System.Globalization;
using System.Runtime.Serialization;
using Spring.Util;

namespace Spring.Collections;

/// <summary>
/// Provides a performance improved hashtable with case-insensitive (string-only! based) key handling.
/// </summary>
/// <author>Erich Eichinger</author>
[Serializable]
public class CaseInsensitiveHashtable : Hashtable
{
    private readonly CultureInfo _culture;

    /// <summary>
    /// Creates a case-insensitive hashtable using <see cref="CultureInfo.CurrentCulture"/>.
    /// </summary>
    public CaseInsensitiveHashtable()
        : this(null, CultureInfo.CurrentCulture)
    {
    }

    /// <summary>
    /// Creates a case-insensitive hashtable using the given <see cref="CultureInfo"/>.
    /// </summary>
    /// <param name="culture">the <see cref="CultureInfo"/> to calculate the hashcode</param>
    public CaseInsensitiveHashtable(CultureInfo culture)
        : this(null, culture)
    {
    }

    /// <summary>
    /// Creates a case-insensitive hashtable using the given <see cref="CultureInfo"/>, initially
    /// populated with entries from another dictionary.
    /// </summary>
    /// <param name="d">the dictionary to copy entries from</param>
    /// <param name="culture">the <see cref="CultureInfo"/> to calculate the hashcode</param>
    public CaseInsensitiveHashtable(IDictionary d, CultureInfo culture)
    {
        AssertUtils.ArgumentNotNull(culture, "culture");
        _culture = culture;

        if (d != null)
        {
            IDictionaryEnumerator enumerator = d.GetEnumerator();
            while (enumerator.MoveNext())
            {
                this.Add(enumerator.Key, enumerator.Value);
            }
        }
    }

    /// <summary>
    /// Initializes a new, empty instance of the <see cref="T:System.Collections.Hashtable"></see> class that is serializable using the specified <see cref="T:System.Runtime.Serialization.SerializationInfo"></see> and <see cref="T:System.Runtime.Serialization.StreamingContext"></see> objects.
    /// </summary>
    /// <param name="context">A <see cref="T:System.Runtime.Serialization.StreamingContext"></see> object containing the source and destination of the serialized stream associated with the <see cref="T:System.Collections.Hashtable"></see>. </param>
    /// <param name="info">A <see cref="T:System.Runtime.Serialization.SerializationInfo"></see> object containing the information required to serialize the <see cref="T:System.Collections.Hashtable"></see> object.</param>
    /// <exception cref="T:System.ArgumentNullException">info is null. </exception>
    protected CaseInsensitiveHashtable(SerializationInfo info, StreamingContext context) : base(info, context)
    {
        string cultureName = info.GetString("_cultureName");
        _culture = new CultureInfo(cultureName);
        AssertUtils.ArgumentNotNull(_culture, "Culture");
    }

    ///<summary>
    ///Implements the <see cref="ISerializable"></see> interface and returns the data needed to serialize the <see cref="CaseInsensitiveHashtable"></see>.
    ///</summary>
    ///
    ///<param name="context">A <see cref="StreamingContext"></see> object containing the source and destination of the serialized stream associated with the <see cref="CaseInsensitiveHashtable"></see>. </param>
    ///<param name="info">A <see cref="SerializationInfo"></see> object containing the information required to serialize the <see cref="CaseInsensitiveHashtable"></see>. </param>
    ///<exception cref="System.ArgumentNullException">info is null. </exception>
    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        base.GetObjectData(info, context);
        info.AddValue("_cultureName", _culture.Name);
    }

    /// <summary>
    /// Calculate the hashcode of the given string key, using the configured culture.
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    protected override int GetHash(object key)
    {
        if (!(key is string)) return key.GetHashCode();
        return _culture.TextInfo.ToLower((string) key).GetHashCode();
    }

    /// <summary>
    /// Compares two keys
    /// </summary>
    protected override bool KeyEquals(object item, object key)
    {
        if (!(key is string))
        {
            return Equals(item, key);
        }

        return 0 == _culture.CompareInfo.Compare((string) item, (string) key, CompareOptions.IgnoreCase);
    }

    /// <summary>
    /// Creates a shallow copy of the current instance.
    /// </summary>
    public override object Clone()
    {
        return new CaseInsensitiveHashtable(this, _culture);
    }
}
